package com.pengpeng.sheepfoldmall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.pengpeng.common.constant.ProductConstant;
import com.pengpeng.sheepfoldmall.product.dao.AttrAttrgroupRelationDao;
import com.pengpeng.sheepfoldmall.product.dao.AttrGroupDao;
import com.pengpeng.sheepfoldmall.product.dao.CategoryDao;
import com.pengpeng.sheepfoldmall.product.entity.*;
import com.pengpeng.sheepfoldmall.product.service.CategoryService;
import com.pengpeng.sheepfoldmall.product.service.ProductAttrValueService;
import com.pengpeng.sheepfoldmall.product.vo.AttrResponseVo;
import com.pengpeng.sheepfoldmall.product.vo.AttrVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pengpeng.common.utils.PageUtils;
import com.pengpeng.common.utils.Query;

import com.pengpeng.sheepfoldmall.product.dao.AttrDao;
import com.pengpeng.sheepfoldmall.product.service.AttrService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("attrService")
public class AttrServiceImpl extends ServiceImpl<AttrDao, AttrEntity> implements AttrService {

    @Autowired
    private AttrAttrgroupRelationDao attrAttrgroupRelationDao;

    @Autowired
    private AttrGroupDao attrGroupDao;

    @Autowired
    private CategoryDao categoryDao;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private ProductAttrValueService productAttrValueService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<AttrEntity> page = this.page(
                new Query<AttrEntity>().getPage(params),
                new QueryWrapper<AttrEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 保存属性，并级联更新 属性及属性分组关系表
     * @param attr
     */
    @Transactional
    @Override
    public void saveAttr(AttrVo attr) {
        //先做属性的基本保存
        AttrEntity attrEntity = new AttrEntity();
        //使用spring的BeanUtils工具类复制属性
        BeanUtils.copyProperties(attr,attrEntity);
        this.save(attrEntity);

        //因为分组id是选填的，所以只有在它非空时，我们才往关系表中插入数据
        //并且只有基本属性才存在属性分组，销售属性不需要往关系表中存
        if (attr.getAttrGroupId() != null && attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
            //再保存 属性及属性分组关系 的相关信息
            AttrAttrgroupRelationEntity attrAttrgroupRelationEntity = new AttrAttrgroupRelationEntity();
            //再mybatis保存一条数据后，会自动返回主键id
            attrAttrgroupRelationEntity.setAttrId(attrEntity.getAttrId());
            //属性分组id是前端返回来的
            attrAttrgroupRelationEntity.setAttrGroupId(attr.getAttrGroupId());
            attrAttrgroupRelationDao.insert(attrAttrgroupRelationEntity);
        }

    }

    /**
     * 查询所有属性的基本分页信息
     * @param params
     * @param catelogId
     * @param attrType
     * @return
     */
    @Override
    public PageUtils queryBaseAttrPage(Map<String, Object> params, Long catelogId, String attrType) {

        //封装查询条件
        //由于销售属性和基本属性是共用一张attr表，所以sql上要拼接上只查询某个属性字段对应的数据（0为基本属性，1为基本属性）
        LambdaQueryWrapper<AttrEntity> queryWrapper = new LambdaQueryWrapper<AttrEntity>()
                .eq(AttrEntity::getAttrType,"base".equalsIgnoreCase(attrType)? ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode():ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());

        //若有分类id则封装分类id作为查询条件
        if (catelogId != 0){
            queryWrapper.eq(AttrEntity::getCatelogId,catelogId);
        }

        //封装查询参数实现模糊查询功能
        String queryKey = (String) params.get("key");
        if (!StringUtils.isEmpty(queryKey)){
            //and后是括号中的多个条件
            queryWrapper.and( obj -> {
                obj.eq(AttrEntity::getAttrId,queryKey).or().like(AttrEntity::getAttrName,queryKey);
            });
        }
        //new Query<>().getPage(params)：将查询参数提取并返回一个page对象
        IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), queryWrapper);
        //分页工具类将MyBatis中的某些数据封装成前端需要的数据
        PageUtils pageUtils = new PageUtils(page);

        //循环遍历每一个属性实体，获取前端需要的 分类名称 和 所属分组名称
        List<AttrResponseVo> collect = page.getRecords().stream().map(item -> {
            AttrResponseVo attrResponseVo = new AttrResponseVo();
            //先将已有数据拷贝到响应的vo对象中
            BeanUtils.copyProperties(item, attrResponseVo);
            //根据属性id获取 属性与属性分组的关系 对象
            AttrAttrgroupRelationEntity attrAttrgroupRelationEntity = attrAttrgroupRelationDao.selectOne(
                    new LambdaQueryWrapper<AttrAttrgroupRelationEntity>()
                            .eq(AttrAttrgroupRelationEntity::getAttrId, item.getAttrId()));

            //若查出的 属性与属性分组的关系 不为空在进行以下逻辑（因为有可能新增属性时没有指定所属分组）
            if (attrAttrgroupRelationEntity != null) {
                //若有的话则根据 属性与属性分组的关系 表中的属性分组id查询属性分组对象并获取属性分组名称设置到返回vo中
                AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrAttrgroupRelationEntity.getAttrGroupId());
                if(attrGroupEntity != null){
                    //分组不为空则获取分组名称
                    attrResponseVo.setGroupName(attrGroupEntity.getAttrGroupName());
                }
            }
            //再根据分类id获取分类实体，获取分类名称并设置到返回vo中
            CategoryEntity categoryEntity = categoryDao.selectById(item.getCatelogId());
            if (categoryEntity != null) {
                attrResponseVo.setCatelogName(categoryEntity.getName());
            }
            return attrResponseVo;
        }).collect(Collectors.toList());

        //将结果更新到分页工具类中
        pageUtils.setList(collect);
        return pageUtils;
    }

    /**
     * 查询属性信息（用于修改回显）
     * @param attrId
     * @return
     */
    @Override
    public AttrResponseVo getAttrInfo(Long attrId) {
        //先拷贝基本数据
        AttrResponseVo attrResponseVo = new AttrResponseVo();
        AttrEntity attrEntity = this.getById(attrId);
        BeanUtils.copyProperties(attrEntity,attrResponseVo);

        //先判断，如果此属性是基本属性才有必要查询分组信息
        if(attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
            //先根据属性id获取 属性与属性分组的关系
            AttrAttrgroupRelationEntity relationEntity = attrAttrgroupRelationDao.selectOne(
                    new LambdaQueryWrapper<AttrAttrgroupRelationEntity>().eq(AttrAttrgroupRelationEntity::getAttrId, attrId));
            //若查出的 属性与属性分组的关系 不为空在进行以下逻辑（因为有可能新增属性时没有指定所属分组）
            if (relationEntity != null){
                //先设置前端需要回显的属性分组id
                attrResponseVo.setAttrGroupId(relationEntity.getAttrGroupId());
                //根据属性分组id设置属性分组名称
                AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());
                attrResponseVo.setGroupName(attrGroupEntity.getAttrGroupName());
            }
        }


        //根据之前写好的查找分类完整路径方法查询到完整路径
        Long[] catelogPath = categoryService.findCatelogPath(attrEntity.getCatelogId());
        attrResponseVo.setCatelogPath(catelogPath);

        //再根据分类id设置分类名称
        CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
        attrResponseVo.setCatelogName(categoryEntity.getName());

        return attrResponseVo;
    }

    /**
     * 修改，并级联更新 属性及属性分组关系表
     * @param attr
     */
    @Transactional
    @Override
    public void updateAttr(AttrVo attr) {
        //先修改基本属性
        AttrEntity attrEntity = new AttrEntity();
        BeanUtils.copyProperties(attr,attrEntity);
        this.updateById(attrEntity);

        //在判断如果此次要修改的属性是基本属性，那还要级联修改关系表中的数据
        if(attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
            //设置好要 新增或修改的 数据
            AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
            relationEntity.setAttrId(attr.getAttrId());
            relationEntity.setAttrGroupId(attr.getAttrGroupId());

            //判断该属性是否在 属性与属性分组关系 表中已存在关系（因为之前新增属性时可能，并未指定属性分组）
            Integer count = attrAttrgroupRelationDao.selectCount(new LambdaQueryWrapper<AttrAttrgroupRelationEntity>()
                    .eq(AttrAttrgroupRelationEntity::getAttrId, attr.getAttrId()));

            //如果存在，则为更新逻辑
            if (count > 0){
                attrAttrgroupRelationDao.update(relationEntity,new LambdaUpdateWrapper<AttrAttrgroupRelationEntity>()
                        .eq(AttrAttrgroupRelationEntity::getAttrId,attr.getAttrId()));
            }else {
                //若不存在，则为新增，不过还是要判断这次的修改有没有选择属性分组，选择了才更新 属性与属性分组关系 表
                if (attr.getAttrGroupId() != null){
                    attrAttrgroupRelationDao.insert(relationEntity);
                }
            }
        }

    }

    /**
     * 获取属性分组下的所有基本属性（用于属性分组关联回显）
     * @param attrGroupId
     * @return
     */
    @Override
    public List<AttrEntity> getAttrRelation(Long attrGroupId) {
        //先根据分组id获取 属性与属性分组关系表 中的数据
        List<AttrAttrgroupRelationEntity> relationEntities = attrAttrgroupRelationDao.selectList(new LambdaQueryWrapper<AttrAttrgroupRelationEntity>()
                .eq(AttrAttrgroupRelationEntity::getAttrGroupId, attrGroupId));

        List<AttrEntity> attrEntities = new ArrayList<>();
        if (!relationEntities.isEmpty()){
            //若数据不为空再去根据属性id去找出对应属性
            List<Long> attrIds = relationEntities.stream().map(item -> {
                return item.getAttrId();
            }).collect(Collectors.toList());
            if (!attrIds.isEmpty()){
                attrEntities = baseMapper.selectBatchIds(attrIds);
            }
        }
        return attrEntities;
    }

    /**
     * 获取属性分组下没有被其他分组关联的的所有基本属性（且只能是本分类下的属性）
     * @param attrGroupId
     * @param params
     * @return
     */
    @Override
    public PageUtils getAttrNoRelation(Long attrGroupId, Map<String, Object> params) {
        //1.当前分组只能关联本分类下的所有属性

        //获取当前分组是那个分类下的，得到他的分类id，确保之后要判断的逻辑都是在当前分组下
        AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrGroupId);
        Long catelogId = attrGroupEntity.getCatelogId();

        //2.当前分组只能关联别的分组没有关联的属性

        //2.1 获取当前分类下的所有分组
        List<AttrGroupEntity> attrGroupEntities = attrGroupDao.selectList(new LambdaQueryWrapper<AttrGroupEntity>()
                .eq(AttrGroupEntity::getCatelogId, catelogId));
        List<Long> attrGroupIds = attrGroupEntities.stream().map(item -> {
            return item.getAttrGroupId();
        }).collect(Collectors.toList());

        //无论当前分类下还有没有其他的分组，都要查询当前分类下的所有基本属性
        LambdaQueryWrapper<AttrEntity> queryWrapper = new LambdaQueryWrapper<AttrEntity>()
                .eq(AttrEntity::getCatelogId,catelogId).eq(AttrEntity::getAttrType,ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode());

        //若当前分类下还包含其他分组，则看看这些分组中有没有关联的基本属性
        if (attrGroupIds != null && attrGroupIds.size() != 0){
            //2.2 获取这些分组关联的属性
            List<AttrAttrgroupRelationEntity> relationEntities = attrAttrgroupRelationDao.selectList(new LambdaQueryWrapper<AttrAttrgroupRelationEntity>()
                    .in(AttrAttrgroupRelationEntity::getAttrGroupId, attrGroupIds));
            List<Long> attrIds = relationEntities.stream().map(item -> {
                return item.getAttrId();
            }).collect(Collectors.toList());

            if (attrIds != null && attrIds.size() != 0){
                //2.3 若有关联的属性，则从当前分类的所有属性中排除这些属性
                queryWrapper.notIn(AttrEntity::getAttrId,attrIds);
            }
        }
        //若有查询条件则封装查询条件
        String key = (String) params.get("key");
        if (!StringUtils.isEmpty(key)){
            queryWrapper.and(item -> {
                item.eq(AttrEntity::getAttrId, key).or().in(AttrEntity::getAttrName, key);
            });
        }
        IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), queryWrapper);
        return new PageUtils(page);
    }


}